package com.beney.server;

import com.beney.codec.MessagePacket;
import io.netty.channel.Channel;
import io.netty.channel.ChannelId;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 用于保存、管理客户端连接过来的 channel
 * 1. 客户端 channel 的 CURD
 * 2. 向客户端 channel 发送消息
 */
@Component
public class ChannelManager {

    /**
     * channel 对应的用户
     */
    private static final AttributeKey<String> CHANNEL_ATTR_KEY_USER = AttributeKey.newInstance("user");

    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * channel map
     * channelid -> channel
     */
    private ConcurrentMap<ChannelId, Channel> channels = new ConcurrentHashMap<>();

    /**
     * channel map
     * username -> channel 通过用户名来进行定位
     */
    private ConcurrentMap<String, Channel> userChannels = new ConcurrentHashMap<>();

    /**
     * 添加 channel (只能通过 channelid 进行查找)
     *
     * @param ch Channel
     */
    public void add(Channel ch) {
        channels.put(ch.id(), ch);
        logger.info("[add][添加新连接 channelId=({})]", ch.id());
    }

    /**
     * 将 channel 绑定到用户 (之后能够通过 channelid 和用户名进行定位)
     *
     * @param ch   Channel
     * @param user 用户名
     */
    public void channelBindUser(Channel ch, String user) {
        Channel e = channels.get(ch.id());
        if (e == null) {
            logger.error("[channelBindUser][连接 ({}) 不存在]", ch.id());
            return;
        }
        // 给 channel 添加属性
        ch.attr(CHANNEL_ATTR_KEY_USER).set(user);
        userChannels.put(user, ch);
    }

    /**
     * 删除 channel
     *
     * @param ch Channel
     */
    public void remove(Channel ch) {
        channels.remove(ch.id());
        if (ch.hasAttr(CHANNEL_ATTR_KEY_USER)) {
            userChannels.remove(ch.attr(CHANNEL_ATTR_KEY_USER).get());
        }
        logger.info("[remove][连接 ({}) 离开]", ch.id());
    }

    /**
     * 向指定用户发送消息
     *
     * @param user   用户名
     * @param packet 消息包
     */
    public void send(String user, MessagePacket packet) {
        Channel ch = userChannels.get(user);
        if (ch == null) {
            logger.error("[send][用户 ({}) 连接不存在]", user);
            return;
        }
        if (!ch.isActive()) {
            logger.error("[send][连接 ({}) 尚未激活", ch.id());
            return;
        }
        ch.writeAndFlush(packet);
    }

    /**
     * 粗糙的群聊发送
     *
     * @param messagePacket .
     */
    public void sendAll(MessagePacket messagePacket) {
        for (Channel ch : channels.values()) {
            if (!ch.isActive()) {
                logger.error("[sendAll][连接 ({}) 尚未激活]", ch.id());
                return;
            }
            ch.writeAndFlush(messagePacket);
        }
    }
}
